﻿// See https://aka.ms/new-console-template for more information

using CShipBasic.AnonymousMethods;
using CShipBasic.attribute;
using CShipBasic.delegates;
using CShipBasic.events;
using CShipBasic.generic;
using CShipBasic.oop;
using CShipBasic.oop.access_specifier;
using CShipBasic.oop.impl;
using CShipBasic.PropertyAndIndex;

internal class Program
{
    private static void Main(string[] args)
    {
        // 1、测试oop相关功能
        //TestOOP();
        
        // 2、测试泛型功能
        //TestGeneric();
        
        // 3、测试委托功能
        //TestDelegate();
        
        // 4、测试事件功能
        //TestEvent();
        
        // 5、测试属性与索引器功能
        //TestPropertyAndIndex();
        
        // 6、测试特性功能
        //TestAttribute();
        
        // 7、测试匿名方法功能
        TestAnonymousMethods();
        
        // 8、测试反射功能

        // 9、测试集合

        // 10、测试数组

        // 11、测试字符串 --- 8.24号 搞完

        // 12、测试多线程功能 --- TODO： 后续慢慢整

        // 13、测试设计模式 --- TODO： 后续慢慢整
    }

    static void TestAnonymousMethods()
    {
        // 匿名方法是通过使用 delegate 关键字创建委托实例来声明的
        // 1、使用匿名方法创建委托实例
        NumberChanger1 NC = delegate(int x){
            Console.WriteLine("Anonymous Method: {0}", x);
        };
        NC(10);
        TestAnonymousMethod.num = 10;
        
        // 2、使用命名方法实例化委托，并调用委托
        NC =  new NumberChanger1(TestAnonymousMethod.AddNum);
        NC(5);
    }
    static void TestPropertyAndIndex()
    {
        // 1、测试属性访问器：访问器（accessor）声明可包含一个 get 访问器、一个 set 访问器，或者同时包含二者
        Student1 student1 = new Student1();
        student1.Name = "张三";
        student1.Age = 20;
        Console.WriteLine("姓名为：{0},年龄为：{1}", student1.Name, student1.Age);
        
        // 2、测试抽象属性：抽象类可拥有抽象属性，这些属性应在派生类中被实现
        Student2 student2 = new Student2();
        student2.Name = "李四";
        student2.Age = 26;
        student2.Code = "001";
        Console.WriteLine(student2.ToString());
        
        // 3、测试索引器
        IndexedNames names = new IndexedNames();
        names[0] = "Zara";
        names[1] = "Riz";
        names[2] = "Nuha";
        names[3] = "Asif";
        names[4] = "Davinder";
        names[5] = "Sunil";
        names[6] = "Rubic";
        // 使用带有 int 参数的第一个索引器
        for (int i = 0; i < IndexedNames.size; i++){
            Console.WriteLine(names[i]);
        }
        // 使用带有 string 参数的第二个索引器
        Console.WriteLine(names["Nuha"]);
        //Console.ReadKey(); // 这句代码 是保证进程不退出
    }
    
    static void TestAttribute()
    {
        // 特性（Attribute）是用于在运行时传递程序中各种元素（比如类、方法、结构、枚举、组件等）的行为信息的声明性标签
        // 一个声明性标签是通过放置在它所应用的元素前面的方括号（[ ]）来描述的
        // 1、测试预定义特性
        TestAttributes.Message("Hello World");
        TestAttributes.OldMethod();
        // 2、构建自定义特性 TODO：
    }
    static void TestEvent()
    {
        // 事件在类中声明且生成，且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器（publisher） 类。
        // 其他接受该事件的类被称为 订阅器（subscriber） 类。
        // 事件使用 发布-订阅（publisher-subscriber） 模型。
        // 在类的内部声明事件，首先必须声明该事件的委托类型,然后，声明事件本身，使用 event 关键字
        // 1、模拟创建一个事件订阅器，订阅消息， 并发送消息
        DelegateEventPublisher delegateEventPublisher = new DelegateEventPublisher();
        // 事件订阅
        delegateEventPublisher.SendMessages += new DelegateEventPublisher.MessageHandler(DelegateEventSubscriber.Print);
        // 通过发布器发布消息
        delegateEventPublisher.MessageProcess();
    }

    static void TestDelegate()
    {
        // 在 C# 中，委托是对方法的引用。委托的最佳用途是用作事件
        // 1、创建委托实例
        NumberChanger numberChanger1 = new NumberChanger(DelegateExample.AddNum);
        // 使用委托对象调用方法
        numberChanger1(10);
        Console.WriteLine("Value of AddNum: {0}", DelegateExample.getNum());
        NumberChanger numberChanger2 = new NumberChanger(DelegateExample.MultNum);
        numberChanger2(2);
        Console.WriteLine("Value of MultNum: {0}", DelegateExample.getNum());
        DelegateExample.num = 10;
        
        // 2、使用委托的多播：委托对象可使用 "+" 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。"-" 运算符可用于从合并的委托中移除组件委托。
        NumberChanger nc;
        NumberChanger nc1 = new NumberChanger(DelegateExample.AddNum);
        NumberChanger nc2 = new NumberChanger(DelegateExample.MultNum);
        nc = nc1;
        nc += nc2;
        // 调用多播？ 
        nc(10);
        Console.WriteLine("Value of 合并委托的多播: {0}", DelegateExample.getNum());
        DelegateExample.num = 10;
        nc -= nc1;
        nc(10);
        Console.WriteLine("Value of 移除委托的多播: {0}", DelegateExample.getNum());
    }
    
    static void TestGeneric()
    {
        // 泛型的概念用于创建通用类和方法
        GenericClass<string> genericClass = new GenericClass<string>("测试字符串类型的泛型");
        // 注意：这里泛型方法的类型和所属泛型类的类型不一定是一样的
        genericClass.Show(1000);
        GenericClass<int> genericClass1 = new GenericClass<int>(2500);
        genericClass1.Show(1800);
        genericClass1.Show("泛型测试完毕～");
    }

    static void TestOOP()
    {
        // 1、测试对象创建和属性赋值
        Student student = new Student();
        student.id = 1;
        student.name = "John";
        Console.WriteLine("student-用户id为:{0}, 用户名为：{1}", student.id, student.name);
        
        // 2、测试带参数的构造函数
        var student1 = new Student(2, "eagle");
        Console.WriteLine("student1-用户id为:{0}, 用户名为：{1}", student1.id, student1.name);
        
        // 3、测试读取和修改静态全局变量
        Console.WriteLine(Student.num);
        Student.num = 200;
        Console.WriteLine(Student.num);
        
        // 4、测试静态方法调用
        Console.WriteLine(Student.testStaticMethod(2,15));
        
        // 5、测试调用结构体
        People people = new People(22, "eagle");
        Console.WriteLine("struct-年龄为：{0}, 姓名为：{1}",people.age, people.name);
        
        // 6、测试枚举
        foreach (EnumExample d in Enum.GetValues(typeof(EnumExample)))
        {
            Console.WriteLine("Enum: "+d);
            Console.WriteLine("Enum type: "+typeof(EnumExample));
        }
        foreach (string d in Enum.GetNames(typeof(EnumExample)))
        {
            Console.WriteLine("Enum: "+d);
            Console.WriteLine("Enum type: "+typeof(EnumExample));
        }
        
        // 7、私有属性值的读取和修改
        Teacher teacher = new Teacher();
        teacher.GetOrSetName = "zhangbao";
        Console.WriteLine("name属性的值为{0}",teacher.GetOrSetName);
        
        // 8、测试继承（字段、方法、多继承）
        // 字段继承
        Programmer programmer = new Programmer();
        Console.WriteLine("该员工的薪资为：{0}，奖金为：{1}",programmer.salary,programmer.bonus);
        // 方法继承
        programmer.Eat();
        // 多级(传递)继承
        MaleProgrammer maleProgrammer = new MaleProgrammer();
        maleProgrammer.Eat();
        
        // 9、聚合测试 - 在 C# 中，聚合是一个类将另一个类定义为任何实体引用的过程
        Address address = new Address();
        address.country="中国";
        address.state = "陕西";
        address.city = "西安";
        // 聚合：就是在当前类中引用另一个类
        Employee employee = new Employee(500,address);
        employee.PrintInfo();
        
        // 10、测试方法重载
        Employee employee1 = new Employee();
        employee1.Eat("eagle");
        employee1.Eat(2000);
        
        // 11、测试方法覆盖-要在 C# 中执行方法覆盖，需要在基类方法中使用 virtual 关键字和 在派生类方法中使用override 关键字
        Programmer programmer1 = new Programmer();
        programmer1.Eat(true);
        
        // 12、测试访问基类属性和方法的关键字base： 
        // - 访问基类字段：如果基类和派生类具有相同的字段，则很有用。如果派生类没有定义相同的字段，则不需要使用 base 关键字。派生类可以直接访问基类字段
        // - 调用基类方法：如果基类和派生类定义相同的方法，这很有用。换句话说，如果方法被覆盖。如果派生类没有定义相同的方法，则不需要使用 base 关键字。派生类的方法可以直接调用基类的方法
        // - 内部调用基类构造函数：每当继承基类时，都会在内部隐式调用基类构造函数
        Programmer programmer2 = new Programmer();
        programmer2.testCallBaseFunction(true);
        Console.WriteLine("测试存在数据成员运行时多态，当前年龄为：{0}",programmer2.age);
        // 注意以下方式：针对属性，会直接调用父类的属性，但是方法，是直接调用派生类的方法
        Employee programmer3 = new Programmer();
        Console.WriteLine("测试存在数据成员运行时多态，当前年龄为：{0}",programmer3.age);
        programmer3.Eat(false);
        
        // 13、测试密封功能
        // - C# sealed 关键字对类和方法应用限制。如果创建密封类，则无法派生它。如果你创建了一个密封的方法，它就不能被覆盖
        // - 结构(Struct)是隐式密封的，因此它们不能被继承
        TestB testB = new TestB(); // sealed修饰的密封类是不能被继承的，所以编译会报错：Error CS0509 : “TestB”: 无法从密封类型“TestA”派生
        // C# 中的密封方法不能被进一步覆盖。密封方法中 必须要sealed与override关键字一起使用。
        // 比如：public sealed override void run(){...}
        
        // 14、测试抽象类
        // - 在C#中，抽象类是声明为抽象的类。它可以有抽象和非抽象方法。它不能被实例化。它的实现必须由派生类提供。这里，派生类被迫提供所有抽象方法的实现。
        // - 注意：不能在抽象方法声明中使用静态和虚拟修饰符
        Shape circle = new Circle();
        circle.draw();
        
        // 15、测试接口
        // - C# 中的接口就像抽象类，因为接口内部声明的所有方法都是抽象方法，不能有方法体，不能实例化。
        // - 用于实现类不能实现的多重继承，它的实现必须由类或结构提供。实现接口的类或结构体，必须提供接口内部声明的所有方法的实现
        // - 实现类中所有实现的接口方法必须是public修饰
        IDrawable rectangle = new Rectangle();
        rectangle.draw();
        
        // 16、访问修饰符测试 - C#中共有5种修饰符
        // public: 可以从任何其他代码访问。
        // private: 仅在同一类或结构体内访问。
        // protected: 仅在同一类和派生类中访问：当前类修饰成protected的属性和方法除了在当前类可以使用外， 只能在派生类中使用。
        // internal: 仅在同一程序集内访问。程序集通常是一个 .dll 或 .exe 文件。这意味着使用 internal 修饰的方法、类或成员只能在声明它们的程序集内部访问，而不能在其他程序集（即使引用了该程序集）中访问。
        // protected internal: 可以在同一程序集内访问，或者从其他程序集的派生类中访问。
        // internal 的实际应用场景：假设我们在开发一个名为 MyLibrary 的库，其中包含一些辅助类和方法，这些辅助功能仅用于库的内部实现，不应该被库的使用者访问。在这种情况下，我们可以使用 internal 来修饰这些类和方法。
        InternalTest internalTest = new InternalTest();
        // Accessing internal variable
        Console.WriteLine("Hello " + internalTest.name);
    }
}